{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Fourier Neural Operator 1D"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\Users\\Harris\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\tqdm\\auto.py:22: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    }
   ],
   "source": [
    "## Imports \n",
    "import matplotlib.pyplot as plt \n",
    "import numpy as np \n",
    "import torch \n",
    "import torch.nn as nn\n",
    "from timeit import default_timer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Set seeds\n",
    "torch.manual_seed(0)\n",
    "np.random.seed(0)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Data"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Heat Equation:\n",
    "\n",
    "\\begin{align*}\n",
    "\\frac{\\partial u}{\\partial t} &= \\alpha \\frac{\\partial^2 u}{\\partial x^2} \\\\\n",
    "u(x, 0) &= x^2 \\\\\n",
    "u(0, t) &= u(1, t) = 2 - \\exp(-t)\n",
    "\\end{align*}\n",
    "\n",
    "Solution: \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "## TODO: Create Heat Equation Class"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "## Steps in x direction\n",
    "nx = 100\n",
    "## Steps in t direction\n",
    "nt = 100\n",
    "\n",
    "h = 1 / nx ## Step size in x direction\n",
    "k = 1 / nt ## Step size in t direction\n",
    "\n",
    "alpha = 0.05\n",
    "r =  alpha * k / h**2\n",
    "\n",
    "X, T = np.meshgrid(np.linspace(0, 1, nx + 1), np.linspace(0, 1, nt + 1))\n",
    "\n",
    "plt.scatter(X, T)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Initial Condition\n",
    "beta = np.pi\n",
    "u0 = np.sin(beta * X[0]) \n",
    "\n",
    "## Boundary Conditions\n",
    "ux0 = 0 # u0[0] # T[:, 0]\n",
    "uxn = 0 # u0[-1] # 2 - np.exp(-T[:, -1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "A = np.zeros((nx - 1, nx - 1))\n",
    "B = np.zeros((nx - 1, nx - 1))\n",
    "\n",
    "for i in range(nx - 1):\n",
    "    A[i, i] = 2 + 2 * r\n",
    "    B[i, i] = 2 - 2 * r\n",
    "\n",
    "for i in range(nx - 2):\n",
    "    A[i + 1, i] = -r\n",
    "    A[i, i + 1] = -r\n",
    "    B[i + 1, i] = r\n",
    "    B[i, i + 1] = r\n",
    "\n",
    "Ainv = np.linalg.inv(A)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Compute Solution\n",
    "u = np.zeros((nt + 1, nx + 1))\n",
    "u[0] = u0\n",
    "u[:, 0] = ux0\n",
    "u[:, -1] = uxn\n",
    "\n",
    "# b = np.zeros(nx - 1)\n",
    "for j in range(1, nt + 1):\n",
    "    # b[0] = r * u[j - 1, 0] + r * u[j, 0]\n",
    "    # b[-1] = r * u[j - 1, -1] + r * u[j, -1]\n",
    "    u[j, 1:nx] = Ainv @ ((B @ u[j - 1, 1:nx])) # + b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.imshow(u, aspect='auto')\n",
    "plt.colorbar()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Number of Samples \n",
    "n = 10000\n",
    "## Output\n",
    "data_in = []\n",
    "data_out = []\n",
    "\n",
    "## Generate Data\n",
    "for _ in range(n):\n",
    "    beta = np.random.uniform(-10, 10)\n",
    "    beta_vec = beta * np.ones(101)\n",
    "\n",
    "    u0 = np.sin(beta * X[0])\n",
    "\n",
    "    data_in.append([beta_vec , u0])\n",
    "\n",
    "    ## Create Solution Matrix\n",
    "    u = np.zeros((nt + 1, nx + 1))\n",
    "    u[0] = u0\n",
    "    u[:, 0] = ux0\n",
    "    u[:, -1] = uxn\n",
    "\n",
    "    # b = np.zeros(nx - 1)\n",
    "    for j in range(1, nt + 1):\n",
    "        # b[0] = r * u[j - 1, 0] + r * u[j, 0]\n",
    "        # b[-1] = r * u[j - 1, -1] + r * u[j, -1]\n",
    "        u[j, 1:nx] = Ainv @ ((B @ u[j - 1, 1:nx])) # + b)\n",
    "\n",
    "    data_out.append(u[-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\Harris\\AppData\\Local\\Temp\\ipykernel_12068\\974941267.py:1: UserWarning: Creating a tensor from a list of numpy.ndarrays is extremely slow. Please consider converting the list to a single numpy.ndarray with numpy.array() before converting to a tensor. (Triggered internally at  C:\\actions-runner\\_work\\pytorch\\pytorch\\builder\\windows\\pytorch\\torch\\csrc\\utils\\tensor_new.cpp:204.)\n",
      "  data_in = np.array(torch.tensor(data_in).float())\n"
     ]
    }
   ],
   "source": [
    "data_in = np.array(torch.tensor(data_in).float())\n",
    "data_out = np.array(torch.tensor(data_out).float().unsqueeze(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Data Loader\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "import pandas as pd\n",
    "\n",
    "class CustomDataset(Dataset):\n",
    "    def __init__(self, t, x):\n",
    "        self.t = t\n",
    "        self.x = x\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.t)\n",
    "    \n",
    "    def __getitem__(self, index):\n",
    "        return self.t[index], self.x[index]\n",
    "\n",
    "data = CustomDataset(data_in, data_out)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 , 0.9762701 ,\n",
       "        0.9762701 ],\n",
       "       [0.        , 0.00976255, 0.01952416, 0.02928391, 0.03904088,\n",
       "        0.04879412, 0.05854271, 0.06828573, 0.07802223, 0.0877513 ,\n",
       "        0.097472  , 0.10718341, 0.11688461, 0.12657467, 0.13625267,\n",
       "        0.14591767, 0.15556878, 0.16520505, 0.17482558, 0.18442944,\n",
       "        0.19401573, 0.20358352, 0.21313192, 0.22266   , 0.23216686,\n",
       "        0.24165158, 0.2511133 , 0.26055104, 0.26996398, 0.27935117,\n",
       "        0.28871176, 0.29804483, 0.30734947, 0.31662482, 0.32587   ,\n",
       "        0.33508414, 0.34426633, 0.3534157 , 0.3625314 , 0.37161252,\n",
       "        0.38065824, 0.3896677 , 0.39863998, 0.4075743 , 0.41646975,\n",
       "        0.4253255 , 0.43414074, 0.4429146 , 0.45164624, 0.46033484,\n",
       "        0.46897957, 0.47757956, 0.48613408, 0.49464226, 0.50310326,\n",
       "        0.51151633, 0.51988065, 0.52819544, 0.53645986, 0.54467314,\n",
       "        0.5528346 , 0.56094325, 0.56899846, 0.5769995 , 0.5849455 ,\n",
       "        0.5928357 , 0.6006695 , 0.608446  , 0.6161645 , 0.6238243 ,\n",
       "        0.63142467, 0.6389648 , 0.646444  , 0.6538617 , 0.66121703,\n",
       "        0.6685093 , 0.6757379 , 0.6829021 , 0.6900012 , 0.6970345 ,\n",
       "        0.70400137, 0.7109012 , 0.7177332 , 0.72449684, 0.73119146,\n",
       "        0.73781633, 0.74437094, 0.75085455, 0.7572666 , 0.7636065 ,\n",
       "        0.7698736 , 0.7760673 , 0.7821871 , 0.7882323 , 0.7942024 ,\n",
       "        0.8000968 , 0.80591494, 0.8116563 , 0.8173202 , 0.8229063 ,\n",
       "        0.82841396]], dtype=float32)"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data[0][0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 101)"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data[0][0].shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataloader = DataLoader(data, batch_size=16, shuffle=True)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Build Network"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using cpu device.\n"
     ]
    }
   ],
   "source": [
    "## Get Device for Training\n",
    "device = 'cuda' if torch.cuda.is_available() else 'cpu'\n",
    "print(f'Using {device} device.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Fourier Integral Kernel 1D\n",
    "class FourierIntegralKernel1D(nn.Module):\n",
    "    def __init__(self, in_channels: int, out_channels: int, modes: int):\n",
    "        super(FourierIntegralKernel1D, self).__init__()\n",
    "        '''\n",
    "        '''\n",
    "        self.in_channels = in_channels\n",
    "        self.out_channels = out_channels \n",
    "        self.modes = modes \n",
    "        ## Set (random) weights for the linear transform\n",
    "        weights = torch.rand(self.modes, self.out_channels, self.in_channels, dtype=torch.cfloat) \n",
    "        self.weights = nn.Parameter(weights / (self.in_channels * self.out_channels)) ## Optional: Scale weights\n",
    "\n",
    "    def forward(self, v: torch.Tensor) -> torch.Tensor:\n",
    "        '''\n",
    "        FFT -> Linear Transform -> Inverse FFT\n",
    "        '''\n",
    "        ## FFT\n",
    "        v_rfft = torch.fft.rfft(v) \n",
    "\n",
    "        ## Linear Transform \n",
    "        lv_rfft = torch.zeros(v_rfft.shape, dtype=torch.cfloat)\n",
    "        lv_rfft[:, :, :self.modes] = torch.einsum('koi, bki -> bko', self.weights, v_rfft[:, :, :self.modes].permute(0, 2, 1)).permute(0, 2, 1) ## TODO: Should I have 5 dimensions here?\n",
    "        \n",
    "        ## Inverse FFT\n",
    "        v2 = torch.fft.irfft(lv_rfft, n=v.shape[-1])\n",
    "        return v2\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Fourier Network Operator 1D\n",
    "class FourierNetworkOperator1D(nn.Module):\n",
    "    def __init__(self, da: int, du: int, width: int, modes: int):\n",
    "        super(FourierNetworkOperator1D, self).__init__()\n",
    "        '''\n",
    "        '''\n",
    "        self.width = width\n",
    "        self.modes = modes\n",
    "\n",
    "        ## P: Lifts the lower dimensional function to higher dimensional space\n",
    "        self.P = nn.Conv1d(da, self.width, 1) ## TODO: Change da\n",
    "\n",
    "        ## K: Fourier integral kernel operator\n",
    "        self.k0 = FourierIntegralKernel1D(self.width, self.width, self.modes)\n",
    "        self.k1 = FourierIntegralKernel1D(self.width, self.width, self.modes)\n",
    "        self.k2 = FourierIntegralKernel1D(self.width, self.width, self.modes)\n",
    "        self.k3 = FourierIntegralKernel1D(self.width, self.width, self.modes)\n",
    "        self.k4 = FourierIntegralKernel1D(self.width, self.width, self.modes)\n",
    "        self.k5 = FourierIntegralKernel1D(self.width, self.width, self.modes)\n",
    "        self.k6 = FourierIntegralKernel1D(self.width, self.width, self.modes)\n",
    "        self.k7 = FourierIntegralKernel1D(self.width, self.width, self.modes)\n",
    "        self.k8 = FourierIntegralKernel1D(self.width, self.width, self.modes)\n",
    "        self.k9 = FourierIntegralKernel1D(self.width, self.width, self.modes)\n",
    "\n",
    "        ## W: Pointwise linear operator\n",
    "        self.w0 = nn.Conv1d(self.width, self.width, 1)\n",
    "        self.w1 = nn.Conv1d(self.width, self.width, 1)\n",
    "        self.w2 = nn.Conv1d(self.width, self.width, 1)\n",
    "        self.w3 = nn.Conv1d(self.width, self.width, 1)\n",
    "        self.w4 = nn.Conv1d(self.width, self.width, 1)\n",
    "        self.w5 = nn.Conv1d(self.width, self.width, 1)\n",
    "        self.w6 = nn.Conv1d(self.width, self.width, 1)\n",
    "        self.w7 = nn.Conv1d(self.width, self.width, 1)\n",
    "        self.w8 = nn.Conv1d(self.width, self.width, 1)\n",
    "        self.w9 = nn.Conv1d(self.width, self.width, 1)\n",
    "\n",
    "        ## Q: Projects the higher dimensional function to lower dimensional space\n",
    "        self.Q = nn.Conv1d(self.width, du, 1) ## TODO: Change du\n",
    "\n",
    "    def forward(self, x: torch.Tensor) -> torch.Tensor:\n",
    "        '''\n",
    "        '''\n",
    "        ## P\n",
    "        x = self.P(x)\n",
    "\n",
    "        ## Fourier Layer #0\n",
    "        ## K\n",
    "        x1 = self.k0(x)\n",
    "        ## W\n",
    "        x2 = self.w0(x)\n",
    "        ## Sum\n",
    "        x = x1 + x2\n",
    "        ## Gelu\n",
    "        x = nn.functional.gelu(x)\n",
    "        # x = nn.functional.gelu(x1)\n",
    "        # x = nn.functional.gelu(x2)\n",
    "\n",
    "        ## Fourier Layer #1\n",
    "        ## K\n",
    "        x1 = self.k1(x)\n",
    "        ## W\n",
    "        x2 = self.w1(x)\n",
    "        ## Sum \n",
    "        x = x1 + x2\n",
    "        ## Gelu\n",
    "        x = nn.functional.gelu(x)\n",
    "        # x = nn.functional.gelu(x1)\n",
    "        # x = nn.functional.gelu(x2)\n",
    "\n",
    "        ## Fourier Layer #2\n",
    "        ## K\n",
    "        x1 = self.k2(x)\n",
    "        ## W\n",
    "        x2 = self.w2(x)\n",
    "        ## Sum\n",
    "        x = x1 + x2\n",
    "        ## Gelu\n",
    "        x = nn.functional.gelu(x)\n",
    "        # x = nn.functional.gelu(x1)\n",
    "        # x = nn.functional.gelu(x2)\n",
    "\n",
    "        ## Fourier Layer #3\n",
    "        ## K\n",
    "        x1 = self.k3(x)\n",
    "        ## W\n",
    "        x2 = self.w3(x)\n",
    "        ## Sum\n",
    "        x = x1 + x2\n",
    "        ## Gelu\n",
    "        x = nn.functional.gelu(x)\n",
    "        # x = nn.functional.gelu(x1)\n",
    "        # x = nn.functional.gelu(x2)\n",
    "\n",
    "        ## Fourier Layer #4\n",
    "        ## K\n",
    "        x1 = self.k4(x)\n",
    "        ## W\n",
    "        x2 = self.w4(x)\n",
    "        ## Sum\n",
    "        x = x1 + x2\n",
    "        ## Gelu\n",
    "        x = nn.functional.gelu(x)\n",
    "        # x = nn.functional.gelu(x1)\n",
    "        # x = nn.functional.gelu(x2)\n",
    "\n",
    "        ## Fourier Layer #5\n",
    "        ## K\n",
    "        x1 = self.k5(x)\n",
    "        ## W\n",
    "        x2 = self.w5(x)\n",
    "        ## Sum\n",
    "        x = x1 + x2\n",
    "        ## Gelu\n",
    "        x = nn.functional.gelu(x)\n",
    "        # x = nn.functional.gelu(x1)\n",
    "        # x = nn.functional.gelu(x2)\n",
    "\n",
    "        ## Fourier Layer #6\n",
    "        ## K\n",
    "        x1 = self.k6(x)\n",
    "        ## W\n",
    "        x2 = self.w6(x)\n",
    "        ## Sum\n",
    "        x = x1 + x2\n",
    "        ## Gelu\n",
    "        x = nn.functional.gelu(x)\n",
    "        # x = nn.functional.gelu(x1)\n",
    "        # x = nn.functional.gelu(x2)\n",
    "\n",
    "        ## Fourier Layer #7\n",
    "        ## K\n",
    "        x1 = self.k7(x)\n",
    "        ## W\n",
    "        x2 = self.w7(x)\n",
    "        ## Sum\n",
    "        x = x1 + x2\n",
    "        ## Gelu\n",
    "        x = nn.functional.gelu(x)\n",
    "        # x = nn.functional.gelu(x1)\n",
    "        # x = nn.functional.gelu(x2)\n",
    "\n",
    "        ## Fourier Layer #8\n",
    "        ## K\n",
    "        x1 = self.k8(x)\n",
    "        ## W\n",
    "        x2 = self.w8(x)\n",
    "        ## Sum\n",
    "        x = x1 + x2\n",
    "        ## Gelu\n",
    "        x = nn.functional.gelu(x)\n",
    "        # x = nn.functional.gelu(x1)\n",
    "        # x = nn.functional.gelu(x2)\n",
    "\n",
    "        ## Fourier Layer #9\n",
    "        ## K\n",
    "        x1 = self.k9(x)\n",
    "        ## W\n",
    "        x2 = self.w9(x)\n",
    "        ## Sum\n",
    "        x = x1 + x2\n",
    "        ## Gelu\n",
    "        x = nn.functional.gelu(x)\n",
    "        # x = nn.functional.gelu(x1)\n",
    "        # x = nn.functional.gelu(x2)\n",
    "\n",
    "        ## Q\n",
    "        x = self.Q(x)\n",
    "        return x\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of parameters: 369537\n"
     ]
    }
   ],
   "source": [
    "import operator\n",
    "from functools import reduce\n",
    "\n",
    "def count_parameters(model):\n",
    "    c = 0\n",
    "    for p in list(model.parameters()):\n",
    "        c += reduce(operator.mul, list(p.size() + (2, ) if p.is_complex() else p.size()))\n",
    "    return c\n",
    "\n",
    "model = FourierNetworkOperator1D(2, 1, width=64, modes=4)\n",
    "print(f'Number of parameters: {count_parameters(model)}')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Train"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Model Parameters\n",
    "learning_rate = 1e-3\n",
    "epochs = 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Loss Function\n",
    "loss_function = nn.MSELoss()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Optimizer \n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "beta: 4.965359625793974\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "## Test 0\n",
    "beta0 = np.random.uniform(-10, 10)\n",
    "print(f'beta: {beta0}')\n",
    "beta0_vec = torch.from_numpy(beta0 * np.ones(101)).float().unsqueeze(0).unsqueeze(0)\n",
    "\n",
    "u0 = torch.from_numpy(np.sin(beta0 * X[0])).float().unsqueeze(0).unsqueeze(0)\n",
    "\n",
    "u = np.zeros((nt + 1, nx + 1))\n",
    "u[0] = u0\n",
    "u[:, 0] = ux0\n",
    "u[:, -1] = uxn\n",
    "\n",
    "b = np.zeros(nx - 1)\n",
    "for j in range(1, nt + 1):\n",
    "    b[0] = r * u[j - 1, 0] + r * u[j, 0]\n",
    "    b[-1] = r * u[j - 1, -1] + r * u[j, -1]\n",
    "    u[j, 1:nx] = Ainv @ ((B @ u[j - 1, 1:nx]) + b)\n",
    "\n",
    "plt.scatter(np.linspace(0, 1, nx + 1), u[-1], alpha=0.5)\n",
    "plt.scatter(np.linspace(0, 1, nx + 1), model(torch.cat((beta0_vec, u0), 1)).detach())\n",
    "# plt.scatter(np.linspace(0, 1, nx + 1), model(beta0_vec).detach())\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "beta: -6.395945853674199\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "## Test 1\n",
    "beta1 = np.random.uniform(-10, 10)\n",
    "print(f'beta: {beta1}')\n",
    "beta1_vec = torch.from_numpy(beta1 * np.ones(101)).float().unsqueeze(0).unsqueeze(0)\n",
    "\n",
    "u0 = torch.from_numpy(np.sin(beta1 * X[0])).float().unsqueeze(0).unsqueeze(0)\n",
    "\n",
    "u = np.zeros((nt + 1, nx + 1))\n",
    "u[0] = u0\n",
    "u[:, 0] = ux0\n",
    "u[:, -1] = uxn\n",
    "\n",
    "b = np.zeros(nx - 1)\n",
    "for j in range(1, nt + 1):\n",
    "    b[0] = r * u[j - 1, 0] + r * u[j, 0]\n",
    "    b[-1] = r * u[j - 1, -1] + r * u[j, -1]\n",
    "    u[j, 1:nx] = Ainv @ ((B @ u[j - 1, 1:nx]) + b)\n",
    "\n",
    "plt.scatter(np.linspace(0, 1, nx + 1), u[-1], alpha=0.5)\n",
    "plt.scatter(np.linspace(0, 1, nx + 1), model(torch.cat((beta1_vec, u0), 1)).detach())\n",
    "# plt.scatter(np.linspace(0, 1, nx + 1), model(beta1_vec).detach())\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "beta: -2.2195371093503447\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "## Test 2\n",
    "beta2 = np.random.uniform(-10, 10)\n",
    "print(f'beta: {beta2}')\n",
    "beta2_vec = torch.from_numpy(beta2 * np.ones(101)).float().unsqueeze(0).unsqueeze(0)\n",
    "\n",
    "u0 = torch.from_numpy(np.sin(beta2 * X[0])).float().unsqueeze(0).unsqueeze(0)\n",
    "\n",
    "u = np.zeros((nt + 1, nx + 1))\n",
    "u[0] = u0\n",
    "u[:, 0] = ux0\n",
    "u[:, -1] = uxn\n",
    "\n",
    "b = np.zeros(nx - 1)\n",
    "for j in range(1, nt + 1):\n",
    "    b[0] = r * u[j - 1, 0] + r * u[j, 0]\n",
    "    b[-1] = r * u[j - 1, -1] + r * u[j, -1]\n",
    "    u[j, 1:nx] = Ainv @ ((B @ u[j - 1, 1:nx]) + b)\n",
    "\n",
    "plt.scatter(np.linspace(0, 1, nx + 1), u[-1], alpha=0.5)\n",
    "plt.scatter(np.linspace(0, 1, nx + 1), model(torch.cat((beta2_vec, u0), 1)).detach())\n",
    "# plt.scatter(np.linspace(0, 1, nx + 1), model(beta2_vec).detach())\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Training Loop\n",
    "def train_loop(dataloader, model, loss_function, optimizer):\n",
    "    size = len(dataloader.dataset)\n",
    "    for batch, (t, x) in enumerate(dataloader):\n",
    "        # Compute prediction and loss\n",
    "        pred = model(t)\n",
    "        loss = loss_function(pred, x)\n",
    "\n",
    "        # Backpropagation\n",
    "        optimizer.zero_grad()\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "        if batch % 100 == 0:\n",
    "            loss, current = loss.item(), batch * len(t)\n",
    "            print(f\"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1\n",
      "-------------------------------\n",
      "loss: 0.035408  [    0/10000]\n",
      "loss: 0.000592  [ 1600/10000]\n",
      "loss: 0.000273  [ 3200/10000]\n",
      "loss: 0.000176  [ 4800/10000]\n",
      "loss: 0.000178  [ 6400/10000]\n",
      "loss: 0.000035  [ 8000/10000]\n",
      "loss: 0.000092  [ 9600/10000]\n",
      "Epoch 2\n",
      "-------------------------------\n",
      "loss: 0.000060  [    0/10000]\n",
      "loss: 0.000041  [ 1600/10000]\n",
      "loss: 0.000052  [ 3200/10000]\n",
      "loss: 0.000028  [ 4800/10000]\n",
      "loss: 0.000084  [ 6400/10000]\n",
      "loss: 0.000151  [ 8000/10000]\n",
      "loss: 0.000159  [ 9600/10000]\n",
      "Epoch 3\n",
      "-------------------------------\n",
      "loss: 0.000176  [    0/10000]\n",
      "loss: 0.000072  [ 1600/10000]\n",
      "loss: 0.000027  [ 3200/10000]\n",
      "loss: 0.000011  [ 4800/10000]\n",
      "loss: 0.000009  [ 6400/10000]\n",
      "loss: 0.000010  [ 8000/10000]\n",
      "loss: 0.000013  [ 9600/10000]\n",
      "Epoch 4\n",
      "-------------------------------\n",
      "loss: 0.000024  [    0/10000]\n",
      "loss: 0.000019  [ 1600/10000]\n",
      "loss: 0.000033  [ 3200/10000]\n",
      "loss: 0.000049  [ 4800/10000]\n",
      "loss: 0.000039  [ 6400/10000]\n",
      "loss: 0.000268  [ 8000/10000]\n",
      "loss: 0.000233  [ 9600/10000]\n",
      "Epoch 5\n",
      "-------------------------------\n",
      "loss: 0.000160  [    0/10000]\n",
      "loss: 0.000223  [ 1600/10000]\n",
      "loss: 0.000093  [ 3200/10000]\n",
      "loss: 0.000021  [ 4800/10000]\n",
      "loss: 0.000014  [ 6400/10000]\n",
      "loss: 0.000033  [ 8000/10000]\n",
      "loss: 0.000006  [ 9600/10000]\n",
      "Done!\n"
     ]
    }
   ],
   "source": [
    "for t in range(epochs):\n",
    "    print(f\"Epoch {t+1}\\n-------------------------------\")\n",
    "    train_loop(dataloader, model, loss_function, optimizer)\n",
    "print(\"Done!\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "## Test 0\n",
    "u0 = torch.from_numpy(np.sin(beta0 * X[0])).float().unsqueeze(0).unsqueeze(0)\n",
    "\n",
    "u = np.zeros((nt + 1, nx + 1))\n",
    "u[0] = u0\n",
    "u[:, 0] = ux0\n",
    "u[:, -1] = uxn\n",
    "\n",
    "b = np.zeros(nx - 1)\n",
    "for j in range(1, nt + 1):\n",
    "    b[0] = r * u[j - 1, 0] + r * u[j, 0]\n",
    "    b[-1] = r * u[j - 1, -1] + r * u[j, -1]\n",
    "    u[j, 1:nx] = Ainv @ ((B @ u[j - 1, 1:nx]) + b)\n",
    "\n",
    "plt.scatter(np.linspace(0, 1, nx + 1), u[-1], alpha=0.5)\n",
    "plt.scatter(np.linspace(0, 1, nx + 1), model(torch.cat((beta0_vec, u0), 1)).detach())\n",
    "# plt.scatter(np.linspace(0, 1, nx + 1), model(beta0_vec).detach())\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "## Test 1\n",
    "u0 = torch.from_numpy(np.sin(beta1 * X[0])).float().unsqueeze(0).unsqueeze(0)\n",
    "\n",
    "u = np.zeros((nt + 1, nx + 1))\n",
    "u[0] = u0\n",
    "u[:, 0] = ux0\n",
    "u[:, -1] = uxn\n",
    "\n",
    "b = np.zeros(nx - 1)\n",
    "for j in range(1, nt + 1):\n",
    "    b[0] = r * u[j - 1, 0] + r * u[j, 0]\n",
    "    b[-1] = r * u[j - 1, -1] + r * u[j, -1]\n",
    "    u[j, 1:nx] = Ainv @ ((B @ u[j - 1, 1:nx]) + b)\n",
    "\n",
    "plt.scatter(np.linspace(0, 1, nx + 1), u[-1], alpha=0.5)\n",
    "plt.scatter(np.linspace(0, 1, nx + 1), model(torch.cat((beta1_vec, u0), 1)).detach())\n",
    "# plt.scatter(np.linspace(0, 1, nx + 1), model(beta1_vec).detach())\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "## Test 2\n",
    "u0 = torch.from_numpy(np.sin(beta2 * X[0])).float().unsqueeze(0).unsqueeze(0)\n",
    "\n",
    "u = np.zeros((nt + 1, nx + 1))\n",
    "u[0] = u0\n",
    "u[:, 0] = ux0\n",
    "u[:, -1] = uxn\n",
    "\n",
    "b = np.zeros(nx - 1)\n",
    "for j in range(1, nt + 1):\n",
    "    b[0] = r * u[j - 1, 0] + r * u[j, 0]\n",
    "    b[-1] = r * u[j - 1, -1] + r * u[j, -1]\n",
    "    u[j, 1:nx] = Ainv @ ((B @ u[j - 1, 1:nx]) + b)\n",
    "\n",
    "plt.scatter(np.linspace(0, 1, nx + 1), u[-1], alpha=0.5)\n",
    "plt.scatter(np.linspace(0, 1, nx + 1), model(torch.cat((beta2_vec, u0), 1)).detach())\n",
    "# plt.scatter(np.linspace(0, 1, nx + 1), model(beta2_vec).detach())\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "9b3b15e79d64212a14c381e1bc9a41101994b32312634469deb1a16fd6054240"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
